#include "../exercise.h"

// READ: 派生类 <https://zh.cppreference.com/w/cpp/language/derived_class>

static int i = 0;

struct X {
    int x;

    X(int x_) : x(x_) {
        std::cout << ++i << ". " << "X(" << x << ')' << std::endl;
    }
    X(X const &other) : x(other.x) {
        std::cout << ++i << ". " << "X(X const &) : x(" << x << ')' << std::endl;
    }
    ~X() {
        std::cout << ++i << ". " << "~X(" << x << ')' << std::endl;
    }
};
struct A {
    int a;

    A(int a_) : a(a_) {
        std::cout << ++i << ". " << "A(" << a << ')' << std::endl;
    }
    A(A const &other) : a(other.a) {
        std::cout << ++i << ". " << "A(A const &) : a(" << a << ')' << std::endl;
    }
    ~A() {
        std::cout << ++i << ". " << "~A(" << a << ')' << std::endl;
    }
};
struct B : public A {
    X x;

    B(int b) : A(1), x(b) {
        std::cout << ++i << ". " << "B(" << a << ", X(" << x.x << "))" << std::endl;
    }
    B(B const &other) : A(other.a), x(other.x) {
        std::cout << ++i << ". " << "B(B const &) : A(" << a << "), x(X(" << x.x << "))" << std::endl;
    }
    ~B() {
        std::cout << ++i << ". " << "~B(" << a << ", X(" << x.x << "))" << std::endl;
    }
};

int main(int argc, char **argv) {
    X x = X(1);
    A a = A(2);
    B b = B(3);

    // TODO: 补全三个类型的大小
    static_assert(sizeof(X) == 4, "There is an int in X");
    static_assert(sizeof(A) == 4, "There is an int in A");
    static_assert(sizeof(B) == 8, "B is an A with an X");

    i = 0;
    std::cout << std::endl
              << "-------------------------" << std::endl
              << std::endl;

    // 这是不可能的，A 无法提供 B 增加的成员变量的值
    // B ba = A(4);

    // 这也是不可能的，因为 A 是 B 的一部分，就好像不可以把套娃的外层放进内层里。
    A ab = B(5);// 然而这个代码可以编译和运行！
    // THINK: 观察打印出的信息，推测把大象放进冰箱分几步？
    // THINK: 这样的代码是“安全”的吗？
    // NOTICE: 真实场景中不太可能出现这样的代码

    i = 0;
    std::cout << std::endl
              << "-------------------------" << std::endl
              << std::endl;

    return 0;
}

/*
斐波那契终于结束了
派生类：c++


运行结果：
1. X(1)
2. A(2)
3. A(1)
4. X(3)
5. B(1, X(3))

-------------------------

1. A(1)
2. X(5)
3. B(1, X(5))
4. A(A const &) : a(1)
5. ~B(1, X(5))
6. ~X(5)
7. ~A(1)

-------------------------

1. ~A(1)
2. ~B(1, X(3))
3. ~X(3)
4. ~A(1)
5. ~A(2)
6. ~X(1)
=================


代码分析
首先，让我们分析代码的结构：

X：简单的类，包含一个int成员
A：简单的类，包含一个int成员
B：继承自A，并包含一个X类型的成员
类型大小计算
根据C++的内存布局规则：

sizeof(X) = sizeof(int) = 4字节
sizeof(A) = sizeof(int) = 4字节
sizeof(B) = sizeof(A) + sizeof(X) + 可能的对齐填充 = 4 + 4 = 8字节

运行结果分析
第一段：普通对象构造
1. X(1)        // X x = X(1);
2. A(2)        // A a = A(2);
3. A(1)        // B b = B(3); 先构造基类A
4. X(3)        // 然后构造成员x
5. B(1, X(3))  // 最后构造B自身
第二段：切片（Slicing）现象
1. A(1)        // B(5) 构造：先构造基类A
2. X(5)        // 然后构造成员x
3. B(1, X(5))  // 最后构造B自身
4. A(A const &) : a(1)  // 切片：将B对象复制到A对象，只复制A部分
5. ~B(1, X(5)) // 析构临时B对象
6. ~X(5)       // 析构临时B对象的x成员
7. ~A(1)       // 析构临时B对象的A基类
8. ~A(1)       // 析构ab对象（main函数结束时）
切片（Slicing）解释
A ab = B(5); 这行代码发生了对象切片：

首先创建一个临时的B(5)对象
然后将这个B对象赋值给A类型的变量ab
由于ab是A类型，只能容纳A部分的数据
B对象中特有的X成员被"切掉"了
安全性问题
这样的代码不安全，因为：

信息丢失：B对象的额外成员数据被丢弃
语义错误：将派生类对象赋值给基类变量可能导致意外的行为
维护困难：容易引起混淆和bug
把大象放进冰箱的步骤
构造大象：创建完整的B对象（包含A基类和X成员）
切片处理：只取出A部分放进"冰箱"（ab变量）
丢弃剩余：B对象特有的部分被丢弃
清理现场：临时B对象被析构
这就是为什么在C++中，通常应该使用指针或引用来处理多态，而不是直接进行值拷贝。

指针、引用虽然也只是获得部分访问空间，但是不会丢弃那些空间；但是值复制只会保留部分数据
*/